#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#include "CProtocol.h"

MODULE_LICENSE("Dual BSD/GPL");

int  paralelport_open(struct inode *inode, struct file *filp);
int paralelport_release(struct inode *inode, struct file *filp);
ssize_t paralelport_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t paralelport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void paralelport_exit(void);
int paralelport_init(void);

struct file_operations paralelport_fops = {
  read: paralelport_read,
  write: paralelport_write,
  open: paralelport_open,
  release: paralelport_release
};

module_init(paralelport_init);
module_exit(paralelport_exit);

int paralelport_major = 61;
int port;

int paralelport_open(struct inode *inode, struct file *filp){
  return 0;
}

int paralelport_release(struct inode *inode, struct file *filp){
  return 0;
}

byte wasEOT;
ssize_t paralelport_read(struct file *filp, char *buf, size_t count, loff_t *f_pos){
	if(wasEOT) {
		wasEOT = 0;
		return 0;
	}

	byte *a = kmalloc(count, GFP_KERNEL);
	byte EOT = 0;
	ssize_t ret = readABunch(a, &EOT, count);
	copy_to_user(buf, a, ret);
	kfree(a);
	
	if(*f_pos == 0){
		*f_pos += ret;
		if(wasEOT) {
			return 0;
		}
		else {
			wasEOT = EOT;
			return ret;
		}
	}
	else
		return 0;
}

ssize_t paralelport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos){
	ssize_t ret = writeABunch(buf, count);
	if(ret > 0) *f_pos += ret;
	return ret;
}

void paralelport_exit(void){
  unregister_chrdev(paralelport_major, "paralelport");

  if (!port) {
    release_region(0x378, 2);
  }

  printk("<1> [paralelport] Removing paralelport module\n");
}

int paralelport_init(void){
  int result;

  result = register_chrdev(paralelport_major, "paralelport", &paralelport_fops);
  if (result < 0){
    printk("<1> [paralelport] cannot obtain major number %d\n", paralelport_major);
    return result;
  }

  port = check_region(0x378, 2);
  if (port){
    printk("<1> [paralelport] cannot reserve 0x378\n");
    result = port;
    paralelport_exit();
    return result;
  }
  request_region(0x378, 2, "paralelport");
  
  //init CProtocol
  initCProtocol();

  printk("<1> [paralelport] Inserting memory module\n");
  return 0;
}
